home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1865 / 1865.xpi / chrome / adblockplus.jar / content / policy.js < prev    next >
Text File  |  2010-01-07  |  18KB  |  615 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is Adblock Plus.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Wladimir Palant.
  18.  * Portions created by the Initial Developer are Copyright (C) 2006-2009
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *
  23.  * ***** END LICENSE BLOCK ***** */
  24.  
  25. /**
  26.  * @fileOverview Content policy implementation, responsible for blocking things.
  27.  * This file is included from AdblockPlus.js.
  28.  */
  29.  
  30. var effectiveTLD = Cc["@mozilla.org/network/effective-tld-service;1"].getService(Ci.nsIEffectiveTLDService);
  31. var netUtils = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil);
  32.  
  33. const ok = Ci.nsIContentPolicy.ACCEPT;
  34. const block = Ci.nsIContentPolicy.REJECT_REQUEST;
  35.  
  36. /**
  37.  * nsIContentPolicy implementation, this gets triggered whenever the browser needs
  38.  * to load something to decide whether the request should be blocked.
  39.  * @class
  40.  */
  41. var policy =
  42. {
  43.     /**
  44.      * Map of content type identifiers by their name.
  45.      * @type Object
  46.      */
  47.     type: null,
  48.     /**
  49.      * Map of content type names by their identifiers (reverse of type map).
  50.      * @type Object
  51.      */
  52.     typeDescr: null,
  53.     /**
  54.      * Map of localized content type names by their identifiers.
  55.      * @type Object
  56.      */
  57.     localizedDescr: null,
  58.     /**
  59.      * Lists the non-visual content types.
  60.      * @type Object
  61.      */
  62.     nonVisual: null,
  63.  
  64.     /**
  65.      * Map containing all schemes that should be ignored by content policy.
  66.      * @type Object
  67.      */
  68.     whitelistSchemes: null,
  69.  
  70.     /**
  71.      * Randomly generated class for object tab nodes.
  72.      * @type String
  73.      */
  74.     objtabClass: null,
  75.     /**
  76.      * Randomly generated class for object tab nodes displayed on top of the object.
  77.      * @type String
  78.      */
  79.     objtabOnTopClass: null,
  80.     /**
  81.      * Randomly generated property name to be set for object tab nodes.
  82.      * @type String
  83.      */
  84.     objtabMarker: null,
  85.     /**
  86.      * Randomly generated class for collapsed nodes.
  87.      * @type String
  88.      */
  89.     collapsedClass: null,
  90.  
  91.     init: function() {
  92.         var types = ["OTHER", "SCRIPT", "IMAGE", "STYLESHEET", "OBJECT", "SUBDOCUMENT", "DOCUMENT", "XBL", "PING", "XMLHTTPREQUEST", "OBJECT_SUBREQUEST", "DTD", "FONT", "MEDIA"];
  93.  
  94.         // type constant by type description and type description by type constant
  95.         this.type = {};
  96.         this.typeDescr = {};
  97.         this.localizedDescr = {};
  98.         var iface = Ci.nsIContentPolicy;
  99.         for each (let typeName in types)
  100.         {
  101.             if ("TYPE_" + typeName in iface)
  102.             {
  103.                 this.type[typeName] = iface["TYPE_" + typeName];
  104.                 this.typeDescr[this.type[typeName]] = typeName;
  105.                 this.localizedDescr[this.type[typeName]] = abp.getString("type_label_" + typeName.toLowerCase());
  106.             }
  107.         }
  108.     
  109.         this.type.BACKGROUND = 0xFFFE;
  110.         this.typeDescr[0xFFFE] = "BACKGROUND";
  111.         this.localizedDescr[0xFFFE] = abp.getString("type_label_background");
  112.  
  113.         this.type.ELEMHIDE = 0xFFFD;
  114.         this.typeDescr[0xFFFD] = "ELEMHIDE";
  115.         this.localizedDescr[0xFFFD] = abp.getString("type_label_elemhide");
  116.  
  117.         this.nonVisual = {};
  118.         for each (let type in ["SCRIPT", "STYLESHEET", "XBL", "PING", "XMLHTTPREQUEST", "OBJECT_SUBREQUEST", "DTD", "FONT"])
  119.             this.nonVisual[this.type[type]] = true;
  120.  
  121.         // whitelisted URL schemes
  122.         this.whitelistSchemes = {};
  123.         for each (var scheme in prefs.whitelistschemes.toLowerCase().split(" "))
  124.             this.whitelistSchemes[scheme] = true;
  125.  
  126.         // Generate identifiers for object tabs
  127.         this.objtabClass = "";
  128.         this.objtabOnTopClass = "";
  129.         this.collapsedClass = "";
  130.         this.objtabMarker = "abpObjTab"
  131.         for (let i = 0; i < 20; i++)
  132.         {
  133.             this.objtabClass += String.fromCharCode("a".charCodeAt(0) + Math.random() * 26);
  134.             this.objtabOnTopClass += String.fromCharCode("a".charCodeAt(0) + Math.random() * 26);
  135.             this.collapsedClass +=  String.fromCharCode("a".charCodeAt(0) + Math.random() * 26);
  136.             this.objtabMarker += String.fromCharCode("a".charCodeAt(0) + Math.random() * 26);
  137.         }
  138.  
  139.         this.initObjectTabCSS();
  140.     },
  141.  
  142.     /**
  143.      * Checks whether a node should be blocked, hides it if necessary
  144.      * @param wnd {nsIDOMWindow}
  145.      * @param node {nsIDOMElement}
  146.      * @param contentType {String}
  147.      * @param location {nsIURI}
  148.      * @param collapse {Boolean} true to force hiding of the node
  149.      * @return {Boolean} false if the node is blocked
  150.      */
  151.     processNode: function(wnd, node, contentType, location, collapse) {
  152.         var topWnd = wnd.top;
  153.         if (!topWnd || !topWnd.location || !topWnd.location.href)
  154.             return true;
  155.  
  156.         var match = null;
  157.         var locationText = location.spec;
  158.         if (!match && prefs.enabled)
  159.         {
  160.             match = this.isWindowWhitelisted(topWnd);
  161.             if (match)
  162.             {
  163.                 filterStorage.increaseHitCount(match);
  164.                 return true;
  165.             }
  166.         }
  167.  
  168.         // Data loaded by plugins should be attached to the document
  169.         if (contentType == this.type.OBJECT_SUBREQUEST && node instanceof Element)
  170.             node = node.ownerDocument;
  171.  
  172.         // Fix type for background images
  173.         if (contentType == this.type.IMAGE && node.nodeType == Node.DOCUMENT_NODE)
  174.             contentType = this.type.BACKGROUND;
  175.  
  176.         // Fix type for objects misrepresented as frames or images
  177.         if (contentType != this.type.OBJECT && (node instanceof Ci.nsIDOMHTMLObjectElement || node instanceof Ci.nsIDOMHTMLEmbedElement))
  178.             contentType = this.type.OBJECT;
  179.  
  180.         let docDomain = this.getHostname(this.getOriginWindow(wnd).location.href);
  181.         if (!match && contentType == this.type.ELEMHIDE)
  182.         {
  183.             match = location;
  184.             locationText = match.text.replace(/^.*?#/, '#');
  185.             location = locationText;
  186.  
  187.             if (!match.isActiveOnDomain(docDomain))
  188.                 return true;
  189.         }
  190.  
  191.         var data = RequestList.getDataForWindow(wnd);
  192.  
  193.         var objTab = null;
  194.         let thirdParty = (contentType == this.type.ELEMHIDE ? false : this.isThirdParty(location, docDomain));
  195.  
  196.         if (!match && prefs.enabled) {
  197.             match = whitelistMatcher.matchesAny(locationText, this.typeDescr[contentType] || "", docDomain, thirdParty);
  198.             if (match == null)
  199.                 match = blacklistMatcher.matchesAny(locationText, this.typeDescr[contentType] || "", docDomain, thirdParty);
  200.  
  201.             if (match instanceof BlockingFilter && node instanceof Element && !(contentType in this.nonVisual))
  202.             {
  203.                 var prefCollapse = (match.collapse != null ? match.collapse : !prefs.fastcollapse);
  204.                 if (collapse || prefCollapse)
  205.                     this.schedulePostProcess(node);
  206.             }
  207.  
  208.             // Add object tab
  209.             if (!match && prefs.frameobjects && contentType == this.type.OBJECT)
  210.             {
  211.                 // Before adding object tabs always check whether one exist already
  212.                 let hasObjectTab = (node.nextSibling && node.nextSibling.getUserData(this.objtabMarker));
  213.                 if (!hasObjectTab)
  214.                 {
  215.                     objTab = node.ownerDocument.createElementNS("http://www.w3.org/1999/xhtml", "a");
  216.                     objTab.setUserData(this.objtabMarker, true, null);
  217.                 }
  218.             }
  219.         }
  220.  
  221.         // Store node data
  222.         var nodeData = data.addNode(node, contentType, docDomain, thirdParty, locationText, match, objTab);
  223.         if (match)
  224.             filterStorage.increaseHitCount(match);
  225.         if (objTab)
  226.             wnd.setTimeout(addObjectTab, 0, topWnd, node, nodeData, objTab);
  227.  
  228.         return !match || match instanceof WhitelistFilter;
  229.     },
  230.  
  231.     /**
  232.      * Nodes scheduled for post-processing (might be null).
  233.      * @type Array of Node
  234.      */
  235.     _scheduledNodes: null,
  236.  
  237.     /**
  238.      * Schedules a node for post-processing.
  239.      * @type Array of Node
  240.      */
  241.     schedulePostProcess: function(node)
  242.     {
  243.         if (this._scheduledNodes)
  244.             this._scheduledNodes.push(node);
  245.         else
  246.         {
  247.             this._scheduledNodes = [node];
  248.             runAsync(this.postProcessNodes, this);
  249.         }
  250.     },
  251.  
  252.     /**
  253.      * Processes nodes scheduled for post-processing (typically hides them).
  254.      * @type Array of Node
  255.      */
  256.     postProcessNodes: function()
  257.     {
  258.         let nodes = this._scheduledNodes;
  259.         this._scheduledNodes = null;
  260.  
  261.         for each (let node in nodes)
  262.         {
  263.             // adjust frameset's cols/rows for frames
  264.             let parentNode = node.parentNode;
  265.             if (parentNode && parentNode instanceof Ci.nsIDOMHTMLFrameSetElement)
  266.             {
  267.                 let hasCols = (parentNode.cols && parentNode.cols.indexOf(",") > 0);
  268.                 let hasRows = (parentNode.rows && parentNode.rows.indexOf(",") > 0);
  269.                 if ((hasCols || hasRows) && !(hasCols && hasRows))
  270.                 {
  271.                     let index = -1;
  272.                     for (let frame = node; frame; frame = frame.previousSibling)
  273.                         if (frame instanceof Ci.nsIDOMHTMLFrameElement || frame instanceof Ci.nsIDOMHTMLFrameSetElement)
  274.                             index++;
  275.             
  276.                     let property = (hasCols ? "cols" : "rows");
  277.                     let weights = parentNode[property].split(",");
  278.                     weights[index] = "0";
  279.                     parentNode[property] = weights.join(",");
  280.                 }
  281.             }
  282.             else
  283.                 node.className += " " + this.collapsedClass;
  284.         }
  285.     },
  286.  
  287.     /**
  288.      * Checks whether the location's scheme is blockable.
  289.      * @param location  {nsIURI}
  290.      * @return {Boolean}
  291.      */
  292.     isBlockableScheme: function(location)
  293.     {
  294.         return !(location.scheme in this.whitelistSchemes);
  295.     },
  296.  
  297.     /**
  298.      * Extracts the hostname from a URL (might return null).
  299.      */
  300.     getHostname: function(/**String*/ url) /**String*/
  301.     {
  302.         try
  303.         {
  304.             return unwrapURL(url).host;
  305.         }
  306.         catch(e)
  307.         {
  308.             return null;
  309.         }
  310.     },
  311.  
  312.     /**
  313.      * If the window doesn't have its own security context (e.g. about:blank or
  314.      * data: URL) walks up the parent chain until a window is found that has a
  315.      * security context.
  316.      */
  317.     getOriginWindow: function(/**Window*/ wnd) /**Window*/
  318.     {
  319.         while (wnd != wnd.parent)
  320.         {
  321.             let uri = abp.makeURL(wnd.location.href);
  322.             if (uri.spec != "about:blank" && !netUtils.URIChainHasFlags(uri, Ci.nsIProtocolHandler.URI_INHERITS_SECURITY_CONTEXT))
  323.                 break;
  324.             wnd = wnd.parent;
  325.         }
  326.         return wnd;
  327.     },
  328.  
  329.     /**
  330.      * Checks whether a page is whitelisted.
  331.      * @param url {String}
  332.      * @return {Boolean}
  333.      */
  334.     isWhitelisted: function(url) {
  335.         return whitelistMatcher.matchesAny(url, "DOCUMENT", this.getHostname(url), false);
  336.     },
  337.  
  338.     /**
  339.      * Checks whether the page loaded in a window is whitelisted.
  340.      * @param wnd {nsIDOMWindow}
  341.      * @return {Boolean}
  342.      */
  343.     isWindowWhitelisted: function(wnd)
  344.     {
  345.         if ("name" in wnd && wnd.name == "messagepane")
  346.         {
  347.             // Thunderbird branch
  348.             try
  349.             {
  350.                 let mailWnd = wnd.QueryInterface(Ci.nsIInterfaceRequestor)
  351.                                                  .getInterface(Ci.nsIWebNavigation)
  352.                                                  .QueryInterface(Ci.nsIDocShellTreeItem)
  353.                                                  .rootTreeItem
  354.                                                  .QueryInterface(Ci.nsIInterfaceRequestor)
  355.                                                  .getInterface(Ci.nsIDOMWindow);
  356.  
  357.                 // Typically we get a wrapped mail window here, need to unwrap
  358.                 try
  359.                 {
  360.                     mailWnd = mailWnd.wrappedJSObject;
  361.                 } catch(e) {}
  362.     
  363.                 if ("currentHeaderData" in mailWnd && "content-base" in mailWnd.currentHeaderData)
  364.                 {
  365.                     return this.isWhitelisted(mailWnd.currentHeaderData["content-base"].headerValue);
  366.                 }
  367.                 else if ("currentHeaderData" in mailWnd && "from" in mailWnd.currentHeaderData)
  368.                 {
  369.                     let emailAddress = headerParser.extractHeaderAddressMailboxes(mailWnd.currentHeaderData.from.headerValue);
  370.                     if (emailAddress)
  371.                     {
  372.                         emailAddress = 'mailto:' + emailAddress.replace(/^[\s"]+/, "").replace(/[\s"]+$/, "").replace(/\s/g, '%20');
  373.                         return this.isWhitelisted(emailAddress);
  374.                     }
  375.                 }
  376.             } catch(e) {}
  377.         }
  378.         else
  379.         {
  380.             // Firefox branch
  381.             return this.isWhitelisted(wnd.location.href);
  382.         }
  383.         return null;
  384.     },
  385.  
  386.     /**
  387.      * Checks whether the location's origin is different from document's origin.
  388.      */
  389.     isThirdParty: function(/**nsIURI*/location, /**String*/ docDomain) /**Boolean*/
  390.     {
  391.         if (!location || !docDomain)
  392.             return true;
  393.  
  394.         try
  395.         {
  396.             return effectiveTLD.getBaseDomain(location) != effectiveTLD.getBaseDomainFromHost(docDomain);
  397.         }
  398.         catch (e)
  399.         {
  400.             // EffectiveTLDService throws on IP addresses, just compare the host name
  401.             let host = "";
  402.             try
  403.             {
  404.                 host = location.host;
  405.             } catch (e) {}
  406.             return host != docDomain;
  407.         }
  408.     },
  409.  
  410.     /**
  411.      * Updates position of an object tab to match the object
  412.      */
  413.     repositionObjectTab: function(/**Element*/ objTab)
  414.     {
  415.         let object = objTab.previousSibling;
  416.         if (!(object instanceof Ci.nsIDOMHTMLObjectElement || object instanceof Ci.nsIDOMHTMLEmbedElement || object instanceof Ci.nsIDOMHTMLAppletElement))
  417.         {
  418.             if (objTab.parentNode)
  419.                 objTab.parentNode.removeChild(objTab);
  420.             return;
  421.         }
  422.  
  423.         let doc = objTab.ownerDocument;
  424.  
  425.         let objectRect = object.getBoundingClientRect();
  426.         let tabRect = objTab.getBoundingClientRect();
  427.  
  428.         let onTop = (objectRect.top > tabRect.bottom - tabRect.top + 5);
  429.         let leftDiff = objectRect.right - tabRect.right;
  430.         let topDiff = (onTop ? objectRect.top - tabRect.bottom : objectRect.bottom - tabRect.top);
  431.  
  432.         objTab.style.setProperty("left", ((parseInt(objTab.style.left) || 0) + leftDiff) + "px", "important");
  433.         objTab.style.setProperty("top", ((parseInt(objTab.style.top) || 0) + topDiff) + "px", "important");
  434.         objTab.className = (onTop ? this.objtabClass + " " + this.objtabOnTopClass : this.objtabClass);
  435.         objTab.style.removeProperty("visibility");
  436.     },
  437.  
  438.     /**
  439.      * Loads objtabs.css on startup and registers it globally.
  440.      */
  441.     initObjectTabCSS: function()
  442.     {
  443.         // Load CSS asynchronously (synchronous loading at startup causes weird issues)
  444.         try {
  445.             let channel = ioService.newChannel("chrome://adblockplus/content/objtabs.css", null, null);
  446.             channel.asyncOpen({
  447.                 data: "",
  448.                 onDataAvailable: function(request, context, stream, offset, count)
  449.                 {
  450.                     let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
  451.                     scriptableStream.init(stream);
  452.                     this.data += scriptableStream.read(count);
  453.                 },
  454.                 onStartRequest: function() {},
  455.                 onStopRequest: function()
  456.                 {
  457.                     let data = this.data.replace(/%%CLASSNAME%%/g, policy.objtabClass).replace(/%%ONTOP%%/g, policy.objtabOnTopClass).replace(/%%COLLAPSED%%/g, policy.collapsedClass);
  458.                     let objtabsCSS = makeURL("data:text/css," + encodeURIComponent(data));
  459.                     Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService)
  460.                                                                                                                     .loadAndRegisterSheet(objtabsCSS, styleService.USER_SHEET);
  461.                     channel = null;
  462.                 },
  463.                 QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver, Ci.nsIStreamListener])
  464.             }, null);
  465.         } catch (e) {}
  466.     },
  467.  
  468.     //
  469.     // nsISupports interface implementation
  470.     //
  471.  
  472.     QueryInterface: abp.QueryInterface,
  473.  
  474.     //
  475.     // nsIContentPolicy interface implementation
  476.     //
  477.  
  478.     shouldLoad: function(contentType, contentLocation, requestOrigin, node, mimeTypeGuess, extra)
  479.     {
  480.         // return unless we are initialized
  481.         if (!this.whitelistSchemes)
  482.             return ok;
  483.  
  484.         if (!node)
  485.             return ok;
  486.  
  487.         // Ignore standalone objects
  488.         if (contentType == this.type.OBJECT && node.ownerDocument && !/^text\/|[+\/]xml$/.test(node.ownerDocument.contentType))
  489.             return ok;
  490.  
  491.         var wnd = getWindow(node);
  492.         if (!wnd)
  493.             return ok;
  494.  
  495.         var location = unwrapURL(contentLocation);
  496.  
  497.         // Interpret unknown types as "other"
  498.         if (!(contentType in this.typeDescr))
  499.             contentType = this.type.OTHER;
  500.  
  501.         if (contentType == this.type.IMAGE && location.spec == "chrome://global/content/abp-dummy-image-request.png")
  502.         {
  503.             let objTab = node.parentNode;
  504.             if (objTab && objTab.getUserData(this.objtabMarker))
  505.                 runAsync(this.repositionObjectTab, this, objTab);
  506.             return block;
  507.         }
  508.  
  509.         // if it's not a blockable type or a whitelisted scheme, use the usual policy
  510.         if (contentType == this.type.DOCUMENT || !this.isBlockableScheme(location))
  511.             return ok;
  512.  
  513.         return (this.processNode(wnd, node, contentType, location, false) ? ok : block);
  514.     },
  515.  
  516.     shouldProcess: function(contentType, contentLocation, requestOrigin, insecNode, mimeType, extra)
  517.     {
  518.         return ok;
  519.     },
  520.  
  521.     //
  522.     // nsIChannelEventSink interface implementation
  523.     //
  524.  
  525.     onChannelRedirect: function(oldChannel, newChannel, flags)
  526.     {
  527.         try {
  528.             let oldLocation = null;
  529.             let newLocation = null;
  530.             try {
  531.                 oldLocation = oldChannel.originalURI.spec;
  532.                 newLocation = newChannel.URI.spec;
  533.             }
  534.             catch(e2) {}
  535.  
  536.             if (!oldLocation || !newLocation || oldLocation == newLocation)
  537.                 return;
  538.  
  539.             // Look for the request both in the origin window and in its parent (for frames)
  540.             let contexts = [getRequestWindow(newChannel)];
  541.             if (!contexts[0])
  542.                 contexts.pop();
  543.             else if (contexts[0] && contexts[0].parent != contexts[0])
  544.                 contexts.push(contexts[0].parent);
  545.  
  546.             let info = null;
  547.             for each (let context in contexts)
  548.             {
  549.                 // Did we record the original request in its own window?
  550.                 let data = RequestList.getDataForWindow(context, true);
  551.                 if (data)
  552.                     info = data.getURLInfo(oldLocation);
  553.  
  554.                 if (info)
  555.                 {
  556.                     let nodes = info.nodes;
  557.                     let node = (nodes.length > 0 ? nodes[nodes.length - 1] : context.document);
  558.  
  559.                     // HACK: NS_BINDING_ABORTED would be proper error code to throw but this will show up in error console (bug 287107)
  560.                     if (!this.processNode(context, node, info.type, newChannel.URI))
  561.                         throw Cr.NS_BASE_STREAM_WOULD_BLOCK;
  562.                     else
  563.                         return;
  564.                 }
  565.             }
  566.         }
  567.         catch (e if (e != Cr.NS_BASE_STREAM_WOULD_BLOCK))
  568.         {
  569.             // We shouldn't throw exceptions here - this will prevent the redirect.
  570.             dump("Adblock Plus: Unexpected error in policy.onChannelRedirect: " + e + "\n");
  571.         }
  572.     },
  573.  
  574.     // Reapplies filters to all nodes of the window
  575.     refilterWindowInternal: function(wnd, start) {
  576.         if (wnd.closed)
  577.             return;
  578.  
  579.         var wndData = RequestList.getDataForWindow(wnd);
  580.         var data = wndData.getAllLocations();
  581.         for (var i = start; i < data.length; i++) {
  582.             if (i - start >= 20) {
  583.                 // Allow events to process
  584.                 runAsync(this.refilterWindowInternal, this, wnd, i);
  585.                 return;
  586.             }
  587.  
  588.             if (!data[i].filter || data[i].filter instanceof WhitelistFilter)
  589.             {
  590.                 let nodes = data[i].clearNodes();
  591.                 for each (let node in nodes)
  592.                 {
  593.                     if (node.getUserData(this.objtabMarker))
  594.                     {
  595.                         // Remove object tabs
  596.                         if (node.parentNode)
  597.                             node.parentNode.removeChild(node);
  598.                     }
  599.                     else
  600.                         this.processNode(wnd, node, data[i].type, makeURL(data[i].location), true);
  601.                 }
  602.             }
  603.         }
  604.  
  605.         wndData.notifyListeners("invalidate", data);
  606.     },
  607.  
  608.     // Calls refilterWindowInternal delayed to allow events to process
  609.     refilterWindow: function(wnd) {
  610.         runAsync(this.refilterWindowInternal, this, wnd, 0);
  611.     }
  612. };
  613.  
  614. abp.policy = policy;
  615.